
package net.flowas.genericcrud.service.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.EntityType;

import net.flowas.genericcrud.RepositoryService;
import net.flowas.genericcrud.dto.PageModdel;
import net.flowas.genericcrud.dto.QueryOption;
import net.flowas.genericcrud.model.FileInfo;
import net.flowas.genericcrud.service.ViewConfig;

/**
 * 抽象数据持久化工具类.
 * @author Administrator
 */
public class JpaRepositoryService implements RepositoryService {
  private EntityManager entityManager;
  private ViewConfig viewConfig;
  
  public void setViewConfig(ViewConfig viewConfig) {
	this.viewConfig = viewConfig;
  }
  @Override
  public Long count(QueryOption<Map<String, Object>> option, Class type) {
	Map<String, Object> conditions=option.getExample();
	CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery cq = cb.createQuery();
    Root entity = cq.from(type);
	List<Predicate> predicate = constructPredicate(conditions, cb, entity);
    cq.select(entityManager.getCriteriaBuilder().count(entity))
        .where(predicate.toArray(new Predicate[conditions.size()]));
    javax.persistence.Query query = entityManager.createQuery(cq);
    return (Long) query.getSingleResult();
  }
  @Override
  public <T> PageModdel<T> queryWithCount(QueryOption<Map<String, Object>> option, Class<T> type) {
	PageModdel<T> page = new PageModdel<>();	
	page.setTotal(count(option, type));
	page.setRows(findRang(option, type));
	page.setCurrentPage(option.getPagingOptions().getCurrentPage());
	page.setPageSize(option.getPagingOptions().getPageSize());
	return page;
  } 
  @Override
  public <T> List<T> findRang(QueryOption<Map<String, Object>> option, Class<T> type) {
	Map<String, Object> conditions=option.getExample();
	int rows=option.getPagingOptions().getPageSize();
    int page=option.getPagingOptions().getCurrentPage();
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<T> cq = cb.createQuery(type);
    Root<T> entity = cq.from(type);
    List<Predicate> predicate = constructPredicate(conditions, cb, entity);
    List<Order> sortOrderList = new ArrayList<>();
    // "remark asc";
    String orderConditions = viewConfig.sortOrder(type);
    if (orderConditions != null) {
      String[] sortOrders = orderConditions.split(",");
      for (String sortOrderText : sortOrders) {
        sortOrderText = sortOrderText.replaceAll("  ", " ");
        String[] pair = sortOrderText.split(" ");
        sortOrderList.add(
            "desc".equals(pair[1]) ? cb.desc(entity.get(pair[0])) : cb.asc(entity.get(pair[0])));
      }
    }
    if(false) {
    	//CompoundSelection<T> sele = cb.cconstruct(type, entity.get("textValue"),entity.get("attributeDefinition").get("description"));
    	cq= cq.multiselect(entity.get("textValue"),entity.get("attributeDefinition").get("description"));
    }else {
		/*
			 * Selection<?> fist = entity.getCompoundSelectionItems().get(0);
			 * entity.getCompoundSelectionItems().clear();
			 * entity.getCompoundSelectionItems().add(fist);
			 */
    	cq = cq.select(entity);
    }
    cq.where(predicate.toArray(new Predicate[conditions.size()]))
    .orderBy(sortOrderList);
    
    javax.persistence.Query query = entityManager.createQuery(cq);    
	query.setMaxResults(rows);    
	query.setFirstResult((page - 1) * rows);
	List list = query.getResultList();
    return query.getResultList();
  }

  @Override
  public Object saveOrUpdate(Object entity) {
    Object merged = entityManager.merge(entity);
    if (entity instanceof FileInfo) {
      //触发查询监听，以便转化地址信息
      //entityManager.refresh(merged);
    }
    return merged;
  }

  @Override
  public void remove(Class type,Object id) {
    Object entity = entityManager.find(type, id);
    entityManager.remove(entity);
  }
  @Override
  public <T> T findById(Class<T> entityClass, Object primaryKey) {
    return entityManager.find(entityClass, primaryKey);
  }
  public void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
  }
  
  private List<Predicate> constructPredicate(Map<String, Object> conditions, CriteriaBuilder cb,
      Root entity) {
    List<Predicate> predicate = new ArrayList<>();
    for (Entry<String, Object> entry : conditions.entrySet()) {
      String[] paths = entry.getKey().split("\\.");
      Path path = entity;
      for (int i = 0; i < paths.length; i++) {
        path = path.get(paths[i]);
      }
      if(entry.getValue() instanceof Collection) {
    	  In in = cb.in(path);  
    	  Iterator iterator = ((Collection) entry.getValue()).iterator(); 
          while (iterator.hasNext()) {  
              in.value(iterator.next());  
          }  
    	  predicate.add(in);
      }else {
    	  predicate.add(cb.equal(path, entry.getValue()));
      }
    }
    return predicate;
  }
  @Override
  public void detach(Object entity) {
    entityManager.detach(entity);
  }

@Override
public Collection<Class<? extends Serializable>> getHandledTypes() {
	HashSet set = new HashSet();
    Iterator<EntityType<?>> entities = entityManager.getMetamodel().getEntities().iterator();
    while (entities.hasNext()) {
      set.add(entities.next().getJavaType());
    }
	return set;
  } 
}
